package com.campus.payment.service;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.campus.payment.config.WechatPayConfig;
import com.campus.payment.dto.RefundRequestDTO;
import com.campus.payment.dto.RefundResponseDTO;
import com.campus.payment.entity.RefundRecord;
import com.campus.payment.mapper.RefundRecordMapper;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * <p>
 * 退款记录服务实现类
 * </p>
 *
 * @author ZhaoYuJie
 * @since 2025-01-15
 */
@Service
@Slf4j
public class RefundRecordService extends ServiceImpl<RefundRecordMapper, RefundRecord> {

    @Autowired
    private WechatPayConfig wechatPayConfig;

    private RefundService wechatRefundService;

    /**
     * 初始化微信退款服务
     */
    @PostConstruct
    public void init() {
        try {
            Config config = new RSAPublicKeyConfig.Builder()
                    .merchantId(wechatPayConfig.getMerchantId())
                    .privateKeyFromPath(wechatPayConfig.getPrivateKeyPath())
                    .merchantSerialNumber(wechatPayConfig.getMerchantSerialNumber())
                    .publicKeyFromPath(wechatPayConfig.getPublicKeyPath())
                    .publicKeyId(wechatPayConfig.getPublicKeyId())
                    .apiV3Key(wechatPayConfig.getApiV3Key())
                    .build();

            // 初始化退款服务
            wechatRefundService = new RefundService.Builder().config(config).build();

            log.info("微信退款服务初始化成功");
        } catch (Exception e) {
            log.error("微信退款服务初始化失败", e);
            throw new RuntimeException("微信退款服务初始化失败", e);
        }
    }

    /**
     * 申请退款
     * @param refundRequestDTO 退款请求参数
     * @return 退款响应结果
     */
    @Transactional(rollbackFor = Exception.class)
    public RefundResponseDTO applyRefund(RefundRequestDTO refundRequestDTO) {
        try {
            // 生成退款单号
            String refundNo = "RF" + System.currentTimeMillis() + IdUtil.randomUUID().substring(0, 8).toUpperCase();

            // 创建退款记录
            RefundRecord refundRecord = new RefundRecord();
            refundRecord.setRefundNo(refundNo);
            refundRecord.setOrderId(refundRequestDTO.getOrderId());
            refundRecord.setRefundAmount(refundRequestDTO.getRefundAmount());
            refundRecord.setRefundReason(refundRequestDTO.getRefundReason());
            refundRecord.setRefundType(refundRequestDTO.getRefundType());
            refundRecord.setWechatTransactionId(refundRequestDTO.getWechatTransactionId());
            refundRecord.setStatus(0); // 处理中
            refundRecord.setOperatorId(refundRequestDTO.getOperatorId());
            refundRecord.setOperatorType(refundRequestDTO.getOperatorType());
            refundRecord.setRemark(refundRequestDTO.getRemark());
            refundRecord.setCreateTime(LocalDateTime.now());
            refundRecord.setUpdateTime(LocalDateTime.now());

            // 保存退款记录
            save(refundRecord);

            // 调用微信支付退款接口
            try {
                Refund wechatRefund = createWechatRefund(refundRequestDTO, refundRecord);
                
                // 更新退款记录的微信退款单号
                refundRecord.setWechatRefundId(wechatRefund.getRefundId());
                updateById(refundRecord);
                
                log.info("退款申请成功，订单号: {}, 退款单号: {}, 微信退款单号: {}", 
                    refundRequestDTO.getOrderId(), refundNo, wechatRefund.getRefundId());
                
            } catch (Exception e) {
                // 微信退款申请失败，更新退款记录状态
                refundRecord.setStatus(2); // 退款失败
                refundRecord.setFailureReason(e.getMessage());
                refundRecord.setUpdateTime(LocalDateTime.now());
                updateById(refundRecord);
                
                log.error("微信退款申请失败，订单号: {}, 退款单号: {}", refundRequestDTO.getOrderId(), refundNo, e);
            }

            // 转换为响应DTO
            RefundResponseDTO responseDTO = new RefundResponseDTO();
            BeanUtils.copyProperties(refundRecord, responseDTO);
            return responseDTO;
            
        } catch (Exception e) {
            log.error("申请退款失败，订单号: {}", refundRequestDTO.getOrderId(), e);
            throw new RuntimeException("申请退款失败: " + e.getMessage());
        }
    }

    /**
     * 创建微信退款请求
     */
    private Refund createWechatRefund(RefundRequestDTO refundRequestDTO, RefundRecord refundRecord) {
        // 构建退款请求
        CreateRequest request = new CreateRequest();
        request.setSubMchid(refundRequestDTO.getSubMchid()); // 子商户号
        request.setTransactionId(refundRequestDTO.getWechatTransactionId()); // 微信支付交易号
        request.setOutRefundNo(refundRecord.getRefundNo()); // 商户退款单号
        request.setReason(refundRecord.getRefundReason()); // 退款原因
        request.setNotifyUrl(wechatPayConfig.getRefundNotifyUrl()); // 退款回调地址
        
        // 设置退款金额
        AmountReq amount = new AmountReq();
        amount.setRefund(refundRecord.getRefundAmount().multiply(new BigDecimal("100")).longValue()); // 退款金额，单位分
        amount.setTotal(refundRecord.getRefundAmount().multiply(new BigDecimal("100")).longValue()); // 原订单金额，单位分（这里简化处理，实际应该传入原订单金额）
        amount.setCurrency("CNY"); // 币种
        request.setAmount(amount);

        //打印退款请求参数
        log.info("微信退款请求参数：{}", JSON.toJSONString(request));
        // 调用微信退款接口
        return wechatRefundService.create(request);
    }

    /**
     * 处理退款回调
     * @param refundId 微信退款单号
     * @param refundStatus 退款状态
     * @param successTime 退款成功时间
     * @return 处理结果
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean handleRefundCallback(String refundId, String refundStatus, String successTime) {
        try {
            // 根据微信退款单号查找退款记录
            RefundRecord refundRecord = lambdaQuery()
                    .eq(RefundRecord::getWechatRefundId, refundId)
                    .one();
            
            if (refundRecord == null) {
                log.error("退款回调处理失败，未找到退款记录，微信退款单号: {}", refundId);
                return false;
            }

            // 更新退款记录状态
            if ("SUCCESS".equals(refundStatus)) {
                refundRecord.setStatus(1); // 退款成功
                if (successTime != null) {
                    // 这里需要根据微信返回的时间格式进行解析
                    refundRecord.setRefundTime(LocalDateTime.now()); // 简化处理，使用当前时间
                }
            } else {
                refundRecord.setStatus(2); // 退款失败
                refundRecord.setFailureReason("微信退款失败，状态: " + refundStatus);
            }
            
            refundRecord.setUpdateTime(LocalDateTime.now());
            boolean result = updateById(refundRecord);
            
            log.info("退款回调处理完成，微信退款单号: {}, 状态: {}, 商户退款单号: {}", 
                refundId, refundStatus, refundRecord.getRefundNo());
            
            return result;
            
        } catch (Exception e) {
            log.error("退款回调处理异常，微信退款单号: {}", refundId, e);
            return false;
        }
    }

    /**
     * 根据订单ID查询退款记录
     * @param orderId 订单ID
     * @return 退款记录
     */
    public RefundResponseDTO getRefundByOrderId(String orderId) {
        RefundRecord refundRecord = lambdaQuery()
                .eq(RefundRecord::getOrderId, orderId)
                .orderByDesc(RefundRecord::getCreateTime)
                .last("LIMIT 1")
                .one();
        
        if (refundRecord == null) {
            return null;
        }
        
        RefundResponseDTO responseDTO = new RefundResponseDTO();
        BeanUtils.copyProperties(refundRecord, responseDTO);
        return responseDTO;
    }

}